package com.example.service.impl;


import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.example.entity.vo.response.WeatherVO;
import com.example.service.WeatherService;
import com.example.utils.Const;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;

/**
 * 作者:xuSen
 * 日期2025/4/27 8:42
 */
@Service
public class WeatherServiceImpl implements WeatherService {

    @Resource
    private RestTemplate restTemplate;
    @Value("${spring.weather.ApiHost}")
    private String key;
    @Value("${spring.weather.ApiKey}")
    private String key1;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 根据经纬度获取天气信息
     * 此方法重写自父类或接口，用于具体实现天气信息的获取
     * 选择重写此方法是因为天气信息的获取可能依赖于具体的地理位置，
     * 不同的实现可能需要处理不同的数据源或API调用
     *
     * @param longitude 经度，表示地理位置的东西位置
     * @param latitude  纬度，表示地理位置的南北位置
     * @return WeatherVO 返回一个包含天气信息的对象
     */
    @Override
    public WeatherVO fetchWeather(double longitude, double latitude) {
        return this.fetchFromWeather(longitude, latitude);
    }

    /**
     * 根据经纬度从天气API获取天气信息，并实现缓存机制以提高效率
     * 首先，根据提供的经纬度查询城市ID，然后根据城市ID获取天气信息
     * 如果在缓存中找到了对应的天气信息，则直接返回缓存数据；否则，调用API获取数据，并将数据存入缓存
     *
     * @param longitude 经度，用于确定地理位置
     * @param latitude  纬度，用于确定地理位置
     * @return WeatherVO对象，包含天气信息，如果没有找到或获取失败则返回null
     */
    private WeatherVO fetchFromWeather(double longitude, double latitude) {
        //根据经纬度获取城市id的封装
        JSONObject jsonObject = this.decompressStringToJson(restTemplate.getForObject(
                "https://" + key + "/geo/v2/city/lookup?location=" + longitude + "," + latitude + "&key=" + key1, byte[].class));
        if (jsonObject == null) return null;
        System.out.println(jsonObject);
//        获取封装对象
        JSONObject location = jsonObject.getJSONArray("location").getJSONObject(0);
//        获取城市id
        int id = location.getInteger("id");
//        测试接口
//        int id = 0;
//        JSONObject location=null;
//        // 获取封装对象
//        JSONArray locationArray = jsonObject.getJSONArray("location");
//        if (locationArray != null && !locationArray.isEmpty()) {
//            // 取数组的第一个元素
//            location = locationArray.getJSONObject(0);
//            System.out.println(location);
//            // 获取城市id
//            String idStr = location.getString("id");
//            if (idStr != null) {
//                id = Integer.parseInt(idStr);
//            }
//        }


    // 构造redis缓存键
    String cacheKey = Const.FORUM_WEATHER_CACHE + id;
    // 尝试从缓存中获取天气信息
    String cache = stringRedisTemplate.opsForValue().get(cacheKey);
        if(cache !=null)

    {
        // 如果缓存存在，则解析缓存数据并返回WeatherVO对象
        return JSONObject.parseObject(cache).to(WeatherVO.class);
    }

    // 如果缓存不存在，则从API获取天气信息
    WeatherVO weatherVO = this.fetchFromApi(id, location);
        if(weatherVO==null)return null;
    // 将从API获取的天气信息存入缓存，设置缓存有效期为1小时
        stringRedisTemplate.opsForValue().set(cacheKey, JSONObject.from(weatherVO).toJSONString(),1,TimeUnit.HOURS);

    // 返回获取的天气信息
        return weatherVO;
}

    /**
     * 根据ID和地点信息从API获取天气信息
     *
     * @param id       地点的ID，用于查询天气信息
     * @param location 地点的详细信息，以JSONObject形式表示
     * @return 返回一个WeatherVO对象，包含从API获取的天气信息如果获取失败或解析错误，返回null
     */
    private WeatherVO fetchFromApi(int id, JSONObject location) {
        WeatherVO weatherVO = new WeatherVO();
        weatherVO.setLocation(location);
        //获取一天天气信息
        JSONObject now = this.decompressStringToJson(restTemplate.getForObject(
                "https://" + key + "/v7/weather/now?location=" + id + "&key=" + key1, byte[].class));
        if (now == null) return null;
        weatherVO.setNow(now.getJSONObject("now"));
        //获取5小时天气信息
        JSONObject hourly = this.decompressStringToJson(restTemplate.getForObject(
                "https://" + key + "/v7/weather/24h?location=" + id + "&key=" + key1, byte[].class));
        if (hourly == null) return null;
        weatherVO.setHourly(new JSONArray(hourly.getJSONArray("hourly").stream().limit(5).toList()));
        return weatherVO;
    }

    /**
     * 将压缩的字节数组解压缩为JSON对象
     * 该方法主要用于解压缩经过GZIP压缩的字节数组，并将其解析为一个JSON对象
     *
     * @param data 压缩的字节数组，预期是通过GZIP压缩的
     * @return 解压缩后的JSON对象，如果解压缩或解析过程中发生错误，则返回null
     */
    private JSONObject decompressStringToJson(byte[] data) {
        // 创建一个输出流，用于存储解压缩后的数据
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            // 创建一个输入流，用于解压缩输入的字节数组
            GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(data));
            // 定义一个缓冲区，用于读取解压缩后的数据
            byte[] buffer = new byte[1024];
            int read;
            // 循环读取解压缩后的数据，直到结束
            while ((read = gzip.read(buffer)) != -1) {
                // 将读取的数据写入输出流中
                byteArrayOutputStream.write(buffer, 0, read);
            }
            // 关闭输入流和输出流
            gzip.close();
            byteArrayOutputStream.close();
            // 将输出流中的数据转换为字符串，并解析为JSON对象返回
            return JSONObject.parseObject(byteArrayOutputStream.toString());
        } catch (IOException e) {
            // 如果发生IO错误，返回null
            return null;
        }
    }
}
